/**
 *  Stream Recorder: Records RealPlayer(tm) streams as MP3s
 *  Copyright (C) 2007  David Griffiths
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package dg.radio2ipod.model;

import dg.radio2ipod.BrowserLauncher;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import dg.radio2ipod.Util;

/**
 * Factory for creating stream objects.
 * @author davidg
 */
public final class StreamFactory {
    private static ResourceBundle resBundle = ResourceBundle.getBundle(
            "dg/radio2ipod/resource/strings");
    
    // Patterns of acceptable URLs
    static final String[] ACCEPTABLE_URLS = {
        ".*\\.ram.*",
        ".*\\.rpm.*",
        ".*\\.ra.*",
        ".*\\.rm.*",
        ".*\\.aif.*",
        ".*\\.mov.*"
    };
    
    // Patterns that correspond to default meta-data values
    static final String[][] METADATA_LOOKUPS = new String[][]{
        {".*\\.bbc\\..*/1xtra/.*", "BBC 1Xtra", "R&B/Soul"},
        {".*\\.bbc\\..*/6music/.*", "BBC 6Music", "Rock"},
        {".*\\.bbc\\..*/asiannet/.*", "BBC Asian Network", "World"},
        {".*\\.bbc\\..*/bbc7/.*", "BBC 7", "Speech"},
        {".*\\.bbc\\..*/radio1/.*", "BBC Radio 1", "Pop"},
        {".*\\.bbc\\..*/radio2/.*", "BBC Radio 2", "Easy_Listening"},
        {".*\\.bbc\\..*/radio3/.*", "BBC Radio 3", "Classical"},
        {".*\\.bbc\\..*/radio4/.*", "BBC Radio 4", "Speech"},
        {".*\\.bbc\\..*/fivelive/.*", "BBC Five Live", "Speech"},
        {".*\\.bbc\\..*/wservice/.*", "BBC World Service", "Speech"},
        {".*\\.bbc\\..*/cymru/.*", "BBC Radio Cymru", "Speech"},
        {".*\\.bbc\\..*/scotland/.*", "BBC Radio Scotland", "Speech"},
        {".*\\.bbc\\..*/ulster/.*", "BBC Radio Ulster", "Speech"},
        {".*\\.bbc\\..*/wales/.*", "BBC Radio Wales", "Speech"}
    };
    
    /**
     * Prevent external instantiation of this class.
     */
    private StreamFactory() {
    }
    
    /**
     * Get a stream objects for the given file.
     * @param fs stream file (e.g. *.ram)
     * @throws java.io.IOException if problem accessing the given file
     * @return stream based on the file
     */
    public static Stream getStream(final File fs) throws IOException {
        String path = fs.getAbsolutePath();
        if (path.endsWith(".ram")) {
            BufferedReader reader = new BufferedReader(new FileReader(fs));
            Stream stream = getStream(reader.readLine());
            Date date = new Date();
            SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
            stream.setTitle(stream.getTitle() + " (" + df.format(date) + ")");
            return stream;
        }
        return new Stream(path);
    }
    
    /**
     * Get a stream objects for the given URL. Has some degree of
     * cleverness in translating URLs (eg if you give it a link
     * to the BBC Radio Player, it can work out where the stream
     * is.
     * @param us URL of the stream
     * @throws java.io.IOException if problem reading from URL
     * @return stream for the given URL
     */
    public static Stream getStream(final URL us) throws IOException {
        Stream stream = getStream(us.toExternalForm());
        if ("http".equals(us.getProtocol())) {
            HttpURLConnection conn = (HttpURLConnection) us.openConnection();
            long d = conn.getDate();
            if (d != 0) {
                Date date = new Date(d);
                SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
                stream.setTitle(stream.getTitle() + " (" + df.format(date)
                + ")");
            }
        }
        return stream;
    }
    
    private static Stream getStream(final String theAddress)
    throws IOException {
        String uss = theAddress;
        boolean isBBC = (uss.indexOf(".bbc.") != -1);
        if (isBBC) {
            boolean isAudioPlayer = (uss.indexOf("aod.shtml?") != -1);
            if (isAudioPlayer) {
                int pos = uss.lastIndexOf('?');
                if (pos != -1) {
                    String content = uss.substring(pos + 1);
                    String mediaStore
                            = "http://www.bbc.co.uk/radio/aod/shows/rpms/";
                    uss = mediaStore + content + ".ram";
                    HttpURLConnection conn = null;
                    URL usNew = new URL(uss);
                    conn = (HttpURLConnection) usNew.openConnection();
                    if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
                        uss = mediaStore + content + ".rpm";
                        usNew = new URL(uss);
                    }
                }
                
            }
        }
        if (uss != null) {
            if (!urlAcceptable(uss)) {
                Util.errorMessage("Sorry \"" + uss
                        + "\" doesn't look like a RealMedia stream.");
                Util.errorMessage(
                        "I will open it for you to investigate further");
                Util.errorMessage(
                        "You might find it's a web page containing the audio links you want");
                BrowserLauncher.openURL(uss);
                throw new IllegalArgumentException(
                        resBundle.getString("Invalid_source:_") + uss);
            }
        }
        Stream stream = new Stream(uss);
        setAutoMetaData(stream);
        if (uss != null) {
            if ((stream.getTitle() == null)
            || (stream.getTitle().length() == 0)) {
                stream.setTitle(Util.initCap(Util.getBaseName(
                        uss.replace('_', ' '))));
                stream.setAlbum(stream.getTitle());
            }
        } else {
            String error = resBundle.getString(
                    "Can't_find_source");
            Util.errorMessage("Sorry - I can't find the audio stream");
            throw new IllegalArgumentException(resBundle.getString(
                    "Can't_find_source"));
        }
        return stream;
    }
    
    private static boolean urlAcceptable(final String u) {
        for (int i = 0; i < ACCEPTABLE_URLS.length; i++) {
            String urlPattern = ACCEPTABLE_URLS[i];
            Pattern pattern = Pattern.compile(urlPattern);
            Matcher matcher = pattern.matcher(u);
            if (matcher.matches()) {
                return true;
            }
        }
        return false;
    }
    
    private static void setAutoMetaData(final Stream stream) {
        for (int i = 0; i < METADATA_LOOKUPS.length; i++) {
            String sourcePattern = METADATA_LOOKUPS[i][0];
            String artist = METADATA_LOOKUPS[i][1];
            String genre = METADATA_LOOKUPS[i][2];
            Pattern pattern = Pattern.compile(sourcePattern);
            Matcher matcher = pattern.matcher(stream.getSource());
            if (matcher.matches()) {
                stream.setArtist(artist);
                stream.setGenre(getGenre(genre));
            }
        }
    }
    
    private static String getGenre(final String g) {
        String genre = resBundle.getString(g);
        if (genre == null) {
            return g;
        }
        return genre;
    }
}
